import 'dart:io';
import 'package:flutter/material.dart';

import '../../basic.dart';

//以dio为基础的下载器
class Downloader {
  static final Downloader _instance = Downloader._internal();

  //提供了一个工厂方法来获取该类的实例
  factory Downloader() => _instance;

  // 通过私有方法_internal()隐藏了构造方法，防止被误创建
  Downloader._internal();

  ///下载文件
  Future<String> downloadFile({
    String? urlPath,
    String? filePath,
    DownloadBean? download,
    bool? overwriteFile,
    CancelToken? cancelToken,
    VoidCallback? downloadStart,
    ProgressCallback? onReceiveProgress,
    VoidCallback? downloadComplete,
    VoidCallback? downloadFail,
    VoidCallback? downloadCancel,
  }) async {
    String? _urlPath = urlPath ?? download?.urlPath;
    String? _filePath = filePath ?? download?.filePath;

    bool _overwriteFile = overwriteFile ?? download?.overwriteFile ?? false;
    CancelToken? _cancelToken = cancelToken ?? download?.cancelToken;

    if ((_urlPath?.isEmpty ?? true) ||
        !(_urlPath?.startsWith("http") ?? false) ||
        (_filePath?.isEmpty ?? true)) {
      downloadFail?.call();
      return '';
    }

    bool closeReceiveProgress = false;

    File file = File(_filePath!);
    try {
      //文件存在
      if (await file.exists()) {
        if (_overwriteFile) {
          file.deleteSync();
        } else {
          if (download != null) {
            download.downloadState = DownloadState.downloaded;
          }
          downloadComplete?.call();
          return _filePath;
        }
      }

      //创建一个temp文件
      String tempFilePath = FileUtil.getTempFilePath(_filePath);
      File tempFile = File(tempFilePath);
      if (tempFile.existsSync()) {
        tempFile.deleteSync();
      }
      await tempFile.create(recursive: true); //创建temp文件

      if (download != null) {
        download.downloadState = DownloadState.downloading;
      }
      downloadStart?.call();
      LogUtil.v('开始下载--->>> 下载地址: $_urlPath -->>>  保存地址: $_filePath -->>>');

      await Dio().download(
        _urlPath!,
        tempFilePath,
        cancelToken: _cancelToken,
        onReceiveProgress: (count, total) async {
          String progress = "0";
          if (total != -1) {
            progress = (count / total * 100).toStringAsFixed(0);
          }
          if (download != null)
            download.updateProgress(count: count, total: total);

          LogUtil.v('下载进度$count / $total --->>> $progress%');

          if (onReceiveProgress != null && !closeReceiveProgress) {
            onReceiveProgress.call(count, total);
          }
        },
      );
      await File(tempFilePath).rename(_filePath);

      if (download != null) {
        download.downloadState = DownloadState.downloaded;
      }
      downloadComplete?.call();
      LogUtil.v(
        '下载完成--->>> 地址: $_filePath -->>> 文件是否存在: ${await file.exists()}',
      );
    } catch (e) {
      if (e is DioError && CancelToken.isCancel(e)) {
        LogUtil.v('下载取消--->>> ${e.toString()}  $_filePath');
        closeReceiveProgress = true; //防止取消之后再回调
        if (download != null) {
          download.downloadState = DownloadState.downloadCancel;
        }
        downloadCancel?.call();
      } else {
        LogUtil.v('下载失败--->>> ${e.toString()}  $_filePath');
        if (download != null) {
          download.downloadState = DownloadState.downloadFail;
        }
        downloadFail?.call();
      }
    } finally {}
    return _filePath;
  }
}

//下载实体类
class DownloadBean {
  String? urlPath; //下载地址
  String? filePath; //本地路径
  final int? id;
  final bool overwriteFile; //覆盖已有文件
  final String? fileDirPath; //父路径
  String? fileName; //名字 xx.jpg
  DownloadState downloadState;
  CancelToken? cancelToken;

  DownloadBean({
    this.urlPath,
    this.filePath,
    this.id,
    this.fileDirPath,
    this.fileName,
    this.overwriteFile = false,
    this.downloadState = DownloadState.notDownloaded,
    bool initDownloadState = true,
  }) {
    if (fileName == null && urlPath != null) {
      Uri parse = Uri.parse(urlPath!);
      fileName = parse.path.substring(1, parse.path.length);
    }

    //初始化filePath
    if (filePath == null) {
      if (fileDirPath != null && fileName != null) {
        filePath = "$fileDirPath/$fileName";
      }
    }

    if (initDownloadState && filePath != null) {
      bool exists = File(filePath!).existsSync();
      if (exists) downloadState = DownloadState.downloaded;
    }
  }

  //初始化cancleToken
  initCancelToken() {
    cancelToken = CancelToken();
  }

  //取消下载
  cancelDownload() {
    if (cancelToken != null) {
      cancelToken?.cancel("cancelled");
      initCancelToken();
    }
  }

  double get total => _total;
  double _total = 0; //总下载字节

  double get count => _count;
  double _count = 0; //已经下载了多少

  double get progress =>
      downloadState == DownloadState.downloaded ? 100 : _progress;
  double _progress = 0; //进度 100%

  //进度百分比
  String get progressPer => downloadState == DownloadState.downloaded
      ? '100%'
      : "${(_progress * 100).toInt()}%";

  //更新下载进度
  updateProgress({required int count, required int total, double? progress}) {
    count = count;
    total = total;
    if (total != -1) {
      _progress = count / total;
    }
  }

  ///清空进度
  cleanProgress() {
    _count = 0;
    _total = 0;
    _progress = 0;
  }

  //是否下载完成
  bool isDownloaded() {
    return downloadState == DownloadState.downloaded;
  }

  //目录是否为null
  bool isNotEmpty() {
    if (fileDirPath?.isEmpty ?? true) return false;
    Directory dir = Directory(fileDirPath!);
    if (!dir.existsSync()) return false;

    List<FileSystemEntity> files = dir.listSync();
    return files.isNotEmpty;
  }
}

//下载状态
enum DownloadState {
  notFile, //无下载文件
  notDownloaded, //未下载
  downloading, //正在下载
  waiting, //等待下载
  pause, //暂停下载
  downloaded, //下载完成
  downloadFail, //下载失败
  downloadCancel, //下载取消
}
